﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Flash2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Register LogOutput process handler
            LOG_OUTPUT = new LOG_OUTPUT_DelegateHandler(LOG_OUTPUT_Function);

            SerialPortInit();
            ProcessingReset();
            ReceiveThreadHandler.Start();
        
            _Set_btnForward_Click_EnableHandler = new Set_btnForward_Click_EnableHandler(Set_btnForward_Click_Enable);

            ConnectionList_Init();
        }

        private void Set_btnForward_Click_Enable(object o)
        {
            btnForward.Enabled = (bool)o;
        }


        private void SerialPortInit()
        {
            serialPortMini1.QueueReceiveEnable();

        }

        MyUtility.FileInfoTypeDef file;
        private void btnOpenFile_Click(object sender, EventArgs e)
        {
            string filePath;
            using (OpenFileDialog dialog = new OpenFileDialog())
            {
                 dialog.Multiselect = false;//允许打开多个文件
                 dialog.InitialDirectory = textFilePath.Text;
                 if (dialog.ShowDialog() == DialogResult.OK)
                 { 
                     file.Path =textFilePath.Text= dialog.FileName; 
                     MyUtility.MyFile.GetFileBinary(ref file);
                     LogOutput("File Path: "+file.Path+"\nFile Size: "+file.Size.ToString()+"\n****************************************");
                 }
            };          
        }


        #region // 日志输出
        private delegate void LOG_OUTPUT_DelegateHandler(bool IsShowTime,string obj);
        LOG_OUTPUT_DelegateHandler LOG_OUTPUT;
        private  void LOG_OUTPUT_Function(bool IsShowTime,string obj)
        {
            string _content="";
            if (IsShowTime)
            {
                _content += DateTime.Now.ToString()+ "\n";
            }
             _content += obj + "\n";
             richTextBoxLog.Text += _content;
        }
        public void LogOutput(string text)
        {
            this.richTextBoxLog.Invoke(LOG_OUTPUT,true ,text);
        }
        public void LogOutput(bool IsShowTime, string text)
        {
            this.richTextBoxLog.Invoke(LOG_OUTPUT,IsShowTime, text);
        }
        #endregion


        Thread ReceiveThreadHandler ;
        bool IsTransfering = false;
        byte[] ProcessPhase = new byte[] { 0, 0 };
        Dictionary<string, byte[]> TemporaryArgs = new Dictionary<string, byte[]>();
        private void ReceiveThread(object obj)
        {
            while (true)
            {              
                Queue<List<byte>> msgQueue = serialPortMini1.MsgQueue;
                while (msgQueue.Count > 0)
                {
                    byte[] msg = msgQueue.Dequeue().ToArray();
//至少包含帧头（2字节）、长度（2字节）、目标地址（2字节）、源地址（2字节）、指令（1字节）、指令参数（1字节）、数据区、校验位（1字节）；根据设计不同而不同
// EA,EF(Header) L1,L2 (Little-endian) 00(Destination Address) 00(Source Address) 00(Instruct) 00 Arguments ******(Data)
                    if (msg[0] == 0xEA && msg[1] == 0xFA)
                    {
                        if (msg[4] != DeviceAddress)
                        {
                            continue;
                        }
                        List<byte> acknowledge = new List<byte>() ;
                        acknowledge.AddRange(new byte[] { 0xEA, 0xEF, 0x00, 0x00, msg[5], 0x00, msg[6], 0x00 });
                        switch (msg[6])
                        {
                            case 0x00: 
                                {   //Boardcast command
                                    // Sponsor->Receiver 0xEA, 0xEF, 0x09, 0x00,0x00(destination),0x00(source),0x00(Boardcast Code), act[ 0x01(Grope), 0x02(Slave reaction)] ,0x00(CRC8)
                                    acknowledge[2] = 0x09; acknowledge[3] = 0x00;
                                    if (msg[7]==0x01)
                                    {
                                        int index = ConnectionList_Comprise(msg[5]);
                                        if (index!=0)
                                        {
                                            ConnectionList_Remove(index);
                                        }
                                        index= ConnectionList_Enroll(1,msg[5]);
                                        
                                      //  LogOutput("Completed a handshake with Address {" + msg[5] + "}");
                                    
                                        ConnectionNode node = ConnectionList[index];
                                        node.ProcessPhase= new byte[] { 0, 02 };
                                        ConnectionList[index] = node;

                                        acknowledge[7] = 0x02;
                                        LogOutput("Capture a handshake requir from Address {" + msg[5] + "}");
                                        serialPortMini1.Write(acknowledge.ToArray());
                                    }
                                    else if (msg[7]==0x02)
                                    {
                                        if (ConnectionList_Comprise(msg[5]) != 0)
                                        {
                                            LogOutput("Completed a handshake with Address {" + msg[5] + "}");
                                            ProcessPhase = new byte[] { 0, 02 };
                                        }
                                    }
                                    else
                                    {

                                         ProcessPhase = new byte[] { 0, 0 };
                                    }
                                }
                                break;
                            case 0x02:

                            case 0x03:

                                break;
                            default:
                                break;
                        }
                    }

                }
                this.ReceiveThreadHandler.Suspend();
//                while (IsAbortReceiveThreadHandler) ;
            }
        }
        private void ProcessingReset()
        {
            if (ReceiveThreadHandler!=null)
            {
                ReceiveThreadHandler.Abort();
            }
            ReceiveThreadHandler = new Thread(ReceiveThread);
            serialPortMini1.ClearReceiveEvent();
            serialPortMini1.SerialPortDataReceived += Data_receive;
            IsTransfering = false;
        }
        private void Data_receive(object obj)
        {
            if (this.ReceiveThreadHandler.ThreadState==ThreadState.Suspended)
            {
                ReceiveThreadHandler.Resume();
            }
        }
        Thread TransferThreadHandler;

 
        private void TransferThread(object obj)
        {
            byte addr = (byte)Convert.ToUInt32(textBoxTargetAddr.Text.Trim());

            if (!DetectDestination(addr))
            {
                LogOutput(false, "Fail to send!");
                btnForward.Invoke(_Set_btnForward_Click_EnableHandler, true);
                goto End;
            }
            if (addr==TemporaryArgs["DAddr"][0])
            {
                TemporaryArgs.Remove("DAddr");
            }


        End: ;
          //  throw new NotImplementedException();
        }
        enum MSG_ArgsTable
        {
            Handshake_Require = 0x01, Handshake_Response = 0x02,

        };
        enum MSG_DedicateTable
        {
            Handshake = 0x00, FileTransfer = 0x02,

        };

        public struct ConnectionNode
        {
            public byte Direction;
            public byte Address;
            public byte Status;
             public byte[] ProcessPhase;
            public ConnectionNode(byte a,byte b,byte c,byte[] d)
            {   
                 this.Direction=a;
                 this.Address = b;
                 this.Status = c;
                 this.ProcessPhase = d;
            }
        };
       
        List<ConnectionNode> ConnectionList = new List<ConnectionNode>();
        void ConnectionList_Init()
        {
            ConnectionList.Clear();
            ConnectionList.Add(new ConnectionNode (0,0,0,new byte[]{0,0}));
        }
        int ConnectionList_Comprise(byte addr)
        {
            for (int i = 1; i < ConnectionList.Count; i++)
            {
                if (ConnectionList[i].Address == addr)
                {
                    return i;
                }
            }
            return 0;
        }
        int ConnectionList_Enroll(byte dir,byte addr)
        {
            ConnectionNode node = new ConnectionNode(dir, addr, 0, new byte[] { 0, 0 });
            ConnectionList.Add(node);
            return ConnectionList.Count-2;
        }
        int ConnectionList_Remove(int index)
        {
            ConnectionList.RemoveAt(index);
            return 0;
        }

        byte DeviceAddress = 0x00;
        int DetecttingTriedTimes = 0;
        bool DetectDestination(int addr)
        {
            byte[] acknowledge = new byte[] { 
                0xEA, 0xFA, 
                0x09, 0x00, 
                (byte)addr, DeviceAddress,
                (byte)MSG_DedicateTable.Handshake, (byte)MSG_ArgsTable.Handshake_Require,
                0x00};
            acknowledge[8] = MyUtility.CRC.CRC8Calculate(acknowledge,(ushort)( acknowledge.Length - 1));

            using (System.Timers.Timer t = new System.Timers.Timer(50))
            {  
                DetecttingTriedTimes = 48; 
                int DetecttingTriedTimes_Copy = DetecttingTriedTimes;
                LogOutput("Start to grope whether terminal exists.");
                t.Elapsed += TimerElapsed_DetectDestination;
                t.Enabled = true;
                t.Start();
              
                while (DetecttingTriedTimes_Copy <= 150)
                {
                    if (DetecttingTriedTimes_Copy != DetecttingTriedTimes)
                    {
                        DetecttingTriedTimes_Copy = DetecttingTriedTimes;                   
                        if (ProcessPhase[0] == 0 && ProcessPhase[1] == 0x02)
                        {
                            LogOutput(false, "Detect Address {" + (byte)addr + "} sucessfully");
                            t.Dispose();
                            return true;
                        }
                        if (DetecttingTriedTimes_Copy % 50 == 0)
                        {
                            serialPortMini1.Write(acknowledge);
                            LogOutput(false, DetecttingTriedTimes_Copy / 50 + " time send handshake to address {" + (byte)addr + "}");
                        }
                    }
                }
                LogOutput(false, "Fail to get a reply from address {" + (byte)addr + "}");
                t.Dispose();
            }               
            return false;  
        }

        private void TimerElapsed_DetectDestination(object sender, System.Timers.ElapsedEventArgs e)
        {
            DetecttingTriedTimes++;
        }

        delegate void Set_btnForward_Click_EnableHandler(object o);
        Set_btnForward_Click_EnableHandler _Set_btnForward_Click_EnableHandler;
        private void btnForward_Click(object sender, EventArgs e)
        {
            if (textBoxTargetAddr.Text==string.Empty)
	        {
		        MessageBox.Show("Target address is empty!!!");
                return;
	        }

            btnForward.Enabled = false;
            if (TransferThreadHandler!=null&&TransferThreadHandler.ThreadState==ThreadState.Running)
            {
                TransferThreadHandler.Abort();
            }
            TransferThreadHandler = new Thread(TransferThread);
            TransferThreadHandler.Start();
           // btnForward.Enabled = true;
            //if (file.Content.Length==0)
            //{
            //    LogOutput("File is empty!!!");
            //    return;
            //}
     /*       if (IsTransfering)
            {
                DialogResult result=MessageBox.Show("A procedure of file transferring is processing!!!\n Abandon and send new one, Please click Yes!\nForsaking the process click \"cancel\"", "Warning!!!", MessageBoxButtons.YesNoCancel);
                if (DialogResult.Yes == result)
                {
                    ProcessingReset();
                    ReceiveThreadHandler.Start();
                    IsTransfering = true;
                }
                else if (result==DialogResult.Cancel)
                {
                    ProcessingReset();
                }
            }
            else
            {
                ProcessingReset();
                ReceiveThreadHandler.Start();
                IsTransfering = true;
            }*/
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            //if (ReceiveThreadHandler != null)
            //{
            //   if (ReceiveThreadHandler.ThreadState==ThreadState.Suspended)
            //    {
            //        this.IsAbortReceiveThreadHandler = true;
            //        this.ReceiveThreadHandler.Resume();                  
            //    }
            //     this.ReceiveThreadHandler.Abort();
            //}
            System.Environment.Exit(0); 
        }

        private void textBoxTargetAddr_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!(char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
            {
                e.Handled = true;
            }
        }

   
    }
}
